use super::{get_ch_pool, get_columns, get_work_path, Result, SourceType};
use arc_swap::ArcSwapOption;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::{fs, sync::Arc};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct JoinTable {
    pub table_name: String,
    pub join_name: String,
    pub key_cols: Vec<String>,
    pub val_cols: Vec<String>,
    #[serde(default)]
    pub where_clause: Option<String>,
    pub imp_source: SourceType,
    pub imp_file_name: String,
}

static JOIN_CONF: Lazy<ArcSwapOption<&'static Vec<JoinTable>>> =
    Lazy::new(|| ArcSwapOption::const_empty());
///初始化join配置
pub async fn init_join_config() -> Result<()> {
    let file_path = format!("{}/config/joins.yaml", get_work_path());
    let conf_str = fs::read_to_string(file_path)?;
    let conf: Vec<JoinTable> = serde_yaml::from_str(&conf_str).unwrap();
    // 创建并加载数据，验证join配置
    for jt in conf.iter() {
        create_join_table(jt).await?;
        load_join_table_data(jt).await?;
    }
    let conf_ref = Box::leak(Box::new(conf));
    JOIN_CONF.store(Some(Arc::new(conf_ref)));
    Ok(())
}
///获取导入文件名称对应的所有join配置
pub fn get_join_conf_by_file_name(fname: &str) -> Vec<&JoinTable> {
    let jconfs = JOIN_CONF.load().clone().unwrap();
    let mut res: Vec<&JoinTable> = Vec::new();
    for conf in jconfs.iter() {
        if conf.imp_file_name == fname {
            res.push(conf);
        }
    }
    res
}
pub async fn create_join_table(jt: &JoinTable) -> Result<()> {
    let cols = get_columns(jt.table_name.as_str()).await?;
    for kc in jt.key_cols.iter() {
        if !cols.iter().any(|(c, _)| {
            if kc.starts_with("toString(") && kc.ends_with(")"){
                let c2 = &kc[9..kc.len()-1];
                return c2==c;
            }
            kc == c
        }) {
            return Err(anyhow::anyhow!(
                "join table:{}配置错误: 表{}不存在列{}",
                jt.join_name,
                jt.table_name.as_str(),
                kc
            )
            .into());
        }
    }
    let mut val_cols:Vec<String>=Vec::new();
    for vc in jt.val_cols.iter() {
        let col=cols.iter().find(|(c, _)| vc == c);
        if let Some((c,st))=col {
            let val_col=format!("{} {}",c,st);
            val_cols.push(val_col);
        }else{
            return Err(anyhow::anyhow!(
                "join table:{}配置错误: 表{}不存在列{}",
                jt.join_name,
                jt.table_name.as_str(),
                vc
            )
            .into());
        }
    }
    let mut client = get_ch_pool().get_handle().await?;
    let sql = format!(
        "create table if not exists {}(UKEY String,{}) ENGINE = Join(ANY,LEFT,UKEY)",
        jt.join_name,
        val_cols.join(",")
    );
    client.execute(&sql).await?;
    let sql =format!("truncate table {}",jt.join_name);
    client.execute(&sql).await?;
    Ok(())
}
pub async fn load_join_table_data(jt: &JoinTable) -> Result<()> {
    let where_clause = if let Some(wc) = jt.where_clause.as_ref() {
        format!(" where {}", wc)
    } else {
        "".to_string()
    };
    let key_cols = if jt.key_cols.len() == 1 {
        jt.key_cols[0].clone()
    } else {
        format!("concat({})", jt.key_cols.join(","))
    };
    let val_cols = jt.val_cols.join(",");
    clear_join_table_data(jt).await?;
    let sql = format!(
        "insert into {}(UKEY,{val_cols}) select {key_cols},{val_cols} from {} {where_clause}",
        jt.join_name, jt.table_name
    );
    let mut client = get_ch_pool().get_handle().await?;
    client.execute(&sql).await?;
    Ok(())
}
pub async fn clear_join_table_data(jt: &JoinTable) -> Result<()> {
    let sql = format!("truncate table {}", jt.join_name);
    let mut client = get_ch_pool().get_handle().await?;
    client.execute(&sql).await?;
    Ok(())
}
///搜索计算表达式关联的join表
pub(super) fn search_calc_ref_join(calc_exp: &str) -> Vec<(&SourceType, &str)> {
    let mut res: Vec<(&SourceType, &str)> = Vec::new();
    if let Some(jconfs) = JOIN_CONF.load().as_ref() {
        for conf in jconfs.iter() {
            if calc_exp.contains(&conf.join_name) {
                res.push((&conf.imp_source, &conf.imp_file_name));
            }
        }
    }
    res
}
